package com.ruoyi.project.platform.agent.basic;

import com.fasterxml.jackson.core.type.TypeReference;

import com.ruoyi.project.platform.agent.constant.Platform;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class RestClientWrapper {
    private static final int CONN_TIMEOUT = 60;
    private static final int READ_TIMEOUT = 60;
    private static final int WRITE_TIMEOUT = 60;
    private static final OkHttpClient OK_HTTP_CLIENT;


    static {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(1, TimeUnit.MINUTES);
        OK_HTTP_CLIENT = builder.build();
    }

    /***********************账户风控接口***************************/

    //当前资产
    public Result<List<AccountDto.BalanceItem>> getCurrentBalance(Credential credential, Platform platform) {
        return get("/accounts/current", new HashMap<String, String>() {{
            put("platform", platform.name());
        }}, credential, new TypeReference<Result<List<AccountDto.BalanceItem>>>() {
        });
    }

    //账户报表历史
    public Result<List<AccountDto.AccountTotal>> report(Credential credential, Platform platform) {
        return get("/accounts/report", new HashMap<String, String>() {{
            put("platform", platform.name());
        }}, credential, new TypeReference<Result<List<AccountDto.AccountTotal>>>() {
        });
    }

    //平均成本
    public Result<List<AccountDto.AvgPrice>> avgPrice(Credential credential, Platform platform, Long start) {
        return get("/accounts/orders/avg", new HashMap<String, String>() {{
            put("platform", platform.name());
            put("start", start + "");
        }}, credential, new TypeReference<Result<List<AccountDto.AvgPrice>>>() {
        });
    }

    //盘口深度分析
    public Result<AccountDto.OrderDepth> depthOrder(Credential credential, Platform platform) {
        return get("/accounts/analyse/depths", new HashMap<String, String>() {{
            put("platform", platform.name());
        }}, credential, new TypeReference<Result<AccountDto.OrderDepth>>() {
        });
    }

    //资金变动
    public Result<AccountDto.UsdtInfo> usdtTotal(Credential credential, Platform platform, Long start) {
        return get("/accounts/usdt_total", new HashMap<String, String>() {{
            put("platform", platform.name());
            put("start", start + "");
        }}, credential, new TypeReference<Result<AccountDto.UsdtInfo>>() {
        });
    }


    /***********************交易所代理接口***************************/
    public Result<AgentResultDto.ATicker> getTicker(Credential credential, Platform platform) {
        return get("/agents/ticker", new HashMap<String, String>() {{
            put("platform", platform.name());
        }}, credential, new TypeReference<Result<AgentResultDto.ATicker>>() {
        });
    }


    public Result<List<AccountDto.OrderItem>> queryFlyOrders(Credential credential, Platform platform, Integer type) {
        return get("/agents/queryFlyOrders", new HashMap<String, String>() {{
            put("platform", platform.name());
            put("type", type + "");
        }}, credential, new TypeReference<Result<List<AccountDto.OrderItem>>>() {
        });
    }

    public Result<List<AgentResultDto.DealedOrderItem>> getDealedOrder(Credential credential, Platform platform, Integer type, Long size,
                                                                       Long page, Long start, Long end) {
        return get("/agents/getDealedOrder", new HashMap<String, String>() {{
            put("platform", platform.name());
            put("type", type + "");
            put("size", size + "");
            put("page", page + "");
            put("start", start + "");
            put("end", end + "");
        }}, credential, new TypeReference<Result<List<AgentResultDto.DealedOrderItem>>>() {
        });
    }

    public Result<String> placeOrder(Credential credential, AccountDto.FixedAction action) {
        return post("/agents/placeOrder", action, credential, new TypeReference<Result<String>>() {
        });
    }

    public Result<String> cancelOrder(Credential credential, Platform platform, Integer type, String orderId) {
        return del("/agents/cancelOrder", new HashMap<String, String>() {{
            put("platform", platform.name());
            put("type", type + "");
            put("orderId", orderId);
        }}, credential, new TypeReference<Result<String>>() {
        });
    }


    /***********************外部机器人（python）***************************/
    //安全停止K线
    public Result<Object> safeStop(Credential credential, Platform platform, Integer delay) {
        return get("/accounts/safe_stop", new HashMap<String, String>() {{
            put("platform", platform.name());
            put("delay", delay + "");
        }}, credential, new TypeReference<Result<Object>>() {
        });
    }

    //配置控盘
    public Result<AccountDto.SafeRange> configAction(Credential credential, AccountDto.Action action) {
        return post("/accounts/config_action", action, credential, new TypeReference<Result<AccountDto.SafeRange>>() {
        });
    }


    /***********************计划下单接口***************************/
    public Result<TriggerDto.TriggerBase> createTrigger(Credential credential, TriggerDto.TriggerBase trigger) {
        return post("/triggers", trigger, credential, new TypeReference<Result<TriggerDto.TriggerBase>>() {
        });
    }


    public Result<List<TriggerDto.TriggerInfo>> listTrigger(Credential credential) {
        return get("/triggers", null, credential, new TypeReference<Result<List<TriggerDto.TriggerInfo>>>() {
        });
    }

    public Result<Object> close(Credential credential, Long id) {
        return get("/triggers/close", new HashMap<String, String>() {{
            put("id", id + "");
        }}, credential, new TypeReference<Result<Object>>() {
        });
    }

    public Result<Object> active(Credential credential, Long id) {
        return get("/triggers/active", new HashMap<String, String>() {{
            put("id", id + "");
        }}, credential, new TypeReference<Result<Object>>() {
        });
    }

    public Result<Object> fresh(Credential credential, Long id) {
        return get("/triggers/fresh", new HashMap<String, String>() {{
            put("id", id + "");
        }}, credential, new TypeReference<Result<Object>>() {
        });
    }

    public Result<Object> delete(Credential credential, Long id) {
        return del("/triggers", new HashMap<String, String>() {{
            put("id", id + "");
        }}, credential, new TypeReference<Result<Object>>() {
        });
    }

    public Result<Object> closeAll(Credential credential) {
        return get("/triggers/closeAll", null, credential, new TypeReference<Result<Object>>() {
        });
    }

    public Result<Object> activeAll(Credential credential) {
        return get("/triggers/activeAll", null, credential, new TypeReference<Result<Object>>() {
        });
    }

    public Result<Object> freshAll(Credential credential) {
        return get("/triggers/freshAll", null, credential, new TypeReference<Result<Object>>() {
        });
    }


    private <T> T post(String uri, Object object, Map<String, String> header, Credential credential, TypeReference<T> ref) {
        return call("POST", uri, object, new HashMap<>(), header, credential, ref);
    }

    private <T> T post(String uri, Object object, Credential credential, TypeReference<T> ref) {
        return call("POST", uri, object, new HashMap<>(), new HashMap<>(), credential, ref);
    }

    private <T> T get(String uri, Map<String, String> params, Credential credential, TypeReference<T> ref) {
        if (params == null) {
            params = new HashMap<>();
        }
        return call("GET", uri, null, params, new HashMap<>(), credential, ref);
    }

    private <T> T del(String uri, Map<String, String> params, Credential credential, TypeReference<T> ref) {
        if (params == null) {
            params = new HashMap<>();
        }
        return call("DELETE", uri, null, params, new HashMap<>(), credential, ref);
    }

    private <T> T call(String method, String uri, Object object, Map<String, String> params,
                       Map<String, String> header, Credential credential,
                       TypeReference<T> ref) {
        try {

            if (credential != null) {
                params.put("api_key", credential.getAccessKey());
                params.put("req_time", Instant.now().getEpochSecond() + "");
                params.put("recv_window", "60");
                params.put("sign", createSignature(method, uri, params, credential));
            }

            Request.Builder builder;
            if ("POST".equals(method)) {
                RequestBody body = RequestBody.create(MediaType.parse("application/json"), JsonUtil.writeValue(object));
                builder = new Request.Builder().url(credential.getRobotHost() + uri + "?" + toQueryString(params)).post(body);
            } else if ("DELETE".equals(method)) {
                builder = new Request.Builder().url(credential.getRobotHost() + uri + "?" + toQueryString(params)).delete();
            } else {
                builder = new Request.Builder().url(credential.getRobotHost() + uri + "?" + toQueryString(params)).get();
            }

            Request request = builder.build();
            Response response = OK_HTTP_CLIENT.newCall(request).execute();
            return JsonUtil.readValue(response.body().string(), ref);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    private String createSignature(String method, String uri, Map<String, String> params, Credential credential) {
        StringBuilder sb = new StringBuilder(1024);
        sb.append(method.toUpperCase()).append('\n')
                .append(uri).append('\n');
        SortedMap<String, String> map = new TreeMap<>(params);
        for (Map.Entry<String, String> entry : map.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key).append('=').append(SignatureUtil.urlEncode(value)).append('&');
        }
        sb.deleteCharAt(sb.length() - 1);

        return SignatureUtil.actualSignature(sb.toString(), credential.getSecretKey());
    }


    private String toQueryString(Map<String, String> params) {
        return params.entrySet().stream().map((entry) -> entry.getKey() + "=" + SignatureUtil.urlEncode(entry.getValue()))
                .collect(Collectors.joining("&"));
    }

    private static OkHttpClient createOkHttpClient() {
        return new OkHttpClient.Builder().connectTimeout(CONN_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS).writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
                .build();
    }


}
